package cn.nr19.jian.factory

import cn.nr19.jian.J2Utils
import cn.nr19.jian.Jian
import cn.nr19.jian.exception.TokenException
import cn.nr19.jian.token.ARRNode
import cn.nr19.jian.token.BooleanNode
import cn.nr19.jian.token.EONJNode
import cn.nr19.jian.token.EONNode
import cn.nr19.jian.token.EToken
import cn.nr19.jian.token.FORX
import cn.nr19.jian.token.NumNode
import cn.nr19.jian.token.Node
import cn.nr19.jian.token.StrNode
import cn.nr19.jian.token.Token
import cn.nr19.jian.token.E2Node
import cn.nr19.jian.token.E3Node
import cn.nr19.jian.token.JsNode
import cn.nr19.jian.token.LayoutNode
import cn.nr19.jian.token.VarNode

class EONFactory : NodeFactory2() {

    companion object{
        fun start(code : String,errorListener : (String)->Unit) : EONNode? {
            if (code.isEmpty()) return EONNode()
            // J2Utils.log("start eon",code)
            try {
                val tokens = TokenFactory().startEON(code)
                return EONFactory().start(tokens)
            }catch (e : TokenException){
                e.printStackTrace()
                J2Utils.log("errrrr",e.message,e.javaClass)
                errorListener.invoke(Jian.errMsg(code,e.message?:"",e.position))
                J2Utils.log("EON解析失败",Jian.errMsg(code,e.message?:"",e.position),code)
                return null
            }

        }
        fun start(tokens: List<Token>) : EONNode {
            return EONFactory().start(tokens)
        }

        fun start2(tokens: List<Token>) : Node{
            return EONFactory().start2(tokens)
        }
    }

    private fun start(tokens: List<Token>) : EONNode{

        this.tokens.addAll(tokens)

        // 左右大括号开始
        var isStartSymbold = false

        if (tokens.isEmpty()) return EONNode()  // 无值
        else return pEon(nextToken())

    }

    private fun start2(tokens: List<Token>) : Node{

        this.tokens.addAll(tokens)

        // 左右大括号开始
        var isStartSymbold = false

        if (tokens.isEmpty()) return EONNode()  // 无值
        else return pZhi(nextToken())

    }



    /**
     * @param isEonjValue pZhi.KEY 为 eonjValue 就返回KEY
     */
    private fun pZhi(token: Token,isEonjValue : Boolean = false) : Node{
        // zhi BOOLEAN | INT | FLOAT | STR  | E3 | E2 | JS | shuzu | forx |   lei | jian |  | funx | leizhonglei | quobj;
        return when(token.type){
            EToken.TRUE -> BooleanNode(true).apply { position = pos }
            EToken.FALSE -> BooleanNode(false).apply { position = pos }
            EToken.INT -> NumNode(token.text.toInt()).apply { position = pos }
            EToken.DOUBLE -> NumNode(token.text.toDouble()).apply { position = pos }
            EToken.Str -> StrNode(token.text).apply { position = pos }
            EToken.j2 -> E3Node(token)
            EToken.e2 -> E2Node(token)
            EToken.js -> JsNode(token)

            // 加减 后面对应的是 num
            EToken.jian,EToken.jia ->{
                nextToken().let {
                    if (it.type == EToken.INT) it.text.toInt().let {
                        return if (token.type == EToken.jian) NumNode(-it) else NumNode(it)
                    }
                    if (it.type == EToken.DOUBLE) it.text.toDouble().let {
                        return if (token.type == EToken.jian) NumNode(-it) else NumNode(it)
                    }
                    else throw TokenException("无效配合 ${token.text}${it.text}",0)
                }

            }


            // 数组
            EToken.zzkh -> pArr(token)

           // forx  '/' (STR | E3 | JS | E2) '/[' (zhi WT?)* ']'
            EToken.ysf -> {
                if (token.text == "/"){
                    pForx(token)
                }else{
                    throw TokenException("无法解析",pos)
                }
            }

            // 子EON
            EToken.start -> pEon(token)

            // 键  key:zhi
            EToken.KEY -> {
                if (!isEonjValue){
                    val eonj = EONJNode(token)
                    nextToken(true,EToken.maohao)
                    eonj.value = pZhi(nextToken(),true)
                    eonj
                }else{
                    VarNode(token)
                }
            }

            // LAYOUT
            EToken.layout -> LayoutNode(token.text)


            // KEY:VALUE，
            else -> {
                J2Utils.log("未知类型 EON",token.type,token.text,pos)
                throw TokenException.inputError("非法字符或字符位置有误 ${token.text}",token)
            }
        }
    }


    // eon { key:value,key:value }
    private fun pEon(startToken: Token) : EONNode{
        val eon = EONNode()
        var nxtoken = startToken

        var isStart = false
        if (nxtoken.type == EToken.start){
            isStart = true
            nxtoken = nextToken()
        }

        while (nxtoken.type == EToken.KEY || nxtoken.type == EToken.Str || nxtoken.type == EToken.INT){


            // key : zhi
            var name = nxtoken.text

            // 下一个理论上需要冒号，但是也可以其他符号，KEY可以允许有特殊符号
            var while2 = true
            while (while2){
                readNextToken()?.let {
                    if (it.type == EToken.maohao) {
                        pos++
                        while2 = false
                    }
                    else if (it.type != EToken.Str){
                        pos++
                        name += it.text
                    }
                    else{
                        throw TokenException.inputError("应输入冒号而不是 ${it.type}(${it.text})",it)
                    }
                }
            }

            // 完成组合
            eon.put(name,pZhi(nextToken()))

            // 继续
            nxtoken = nextToken()

            // 为逗号就下一个  不用逗号也可以下一个，逗号并不是必须的
            if (nxtoken.type == EToken.douhao) nxtoken = nextToken()
        }

        // 需闭包
        if (isStart && nxtoken.type != EToken.end){
            throw exception()
        }

        return eon
    }

    // token 必须为 首个 /
    private fun pForx(startToken :Token) : FORX{

        // '/' (STR | E3 | JS | E2) '/[' (zhi WT?)* ']'
        nextToken().let {

            val fx = FORX()
            fx.left = when(it.type){
                EToken.Str -> StrNode(it.text)
                EToken.j2 -> E3Node(it)
                EToken.e2 -> E2Node(it)
                EToken.js -> JsNode(it)
                else -> {
                    J2Utils.log("for2",it)
                    throw exception()
                }
            }

            // 右边 /
            nextToken(true,EToken.ysf)

            // 右边是 EON   /jian/[var]
            pZhi(nextToken()).let {
                if (it is ARRNode) fx.right.datas.addAll(it.datas)
                else fx.right.add(it)
            }
            return fx
        }

    }

    // token 为 [  数组首个开始字符  '[' (zhi ,)* ']' ;
    private fun pArr(startToken: Token) : ARRNode {
        // startToken 需确定为 [  这里就不再次确定了

        val arrNode = ARRNode()
        arrNode.position = pos

        // 只要不是结束符，就一直找新节点，记录节点
        // a+b
        var nxtoken = nextToken()
        while (nxtoken.type != EToken.yzkh){

            // 得到节点
            arrNode.put(pZhi(nxtoken))

            // 继续
            nxtoken = nextToken()

            // 为逗号就下一个  不用逗号也可以下一个，逗号并不是必须的
            if (nxtoken.type == EToken.douhao) nxtoken = nextToken()
        }

        return arrNode
    }

    private fun exception() : TokenException = TokenException("无法解析",pos)


}